home *** CD-ROM | disk | FTP | other *** search
- /*
- SNEWS 2.0
-
- snews - a simple threaded news reader
-
-
- Copyright (C) 1991 John McCombs, Christchurch, NEW ZEALAND
- john@ahuriri.gen.nz
- PO Box 2708, Christchurch, NEW ZEALAND
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 1, as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- See the file COPYING, which contains a copy of the GNU General
- Public License.
-
- Atari version ported by Graham Judd - gjudd@siward.demon.co.uk
-
- */
-
- /*---------------------------- Source Control ------------------------------*/
-
- /*
- * $Id: snews2.c,v 1.2 1994/02/05 18:52:38 gbj Exp user $
- */
-
-
- /****************************************************************************
- * 20 May 92 1.2 GT ka9q mods. *
- * 22 May 92 1.3 GT Set time zone. *
- * 05 Jun 92 1.4 GT Nikki Locke's SPACE command. *
- * 06 Jun 92 1.5 GT Invalidate freed pointers. *
- * 09 Jun 92 1.6 GT Right and left cursor keys. *
- * 12 Jun 92 1.7 NJL Add v_init call & change to use scr_lines. *
- * Add 'q' as alternative to ESC throughout. *
- * Fix memory leak when Escaping from read_thread. *
- * Ensure strtok() accepts tab as well as space. *
- * 17 Jul 92 1.8 GT C++ compilation. *
- * Heap debugging. *
- * Swap out when executing child process. *
- * 16 Aug 92 1.9 MSM Snews 1.9 *
- * Lock history etc. during use *
- * 31 May 93 1.10 MSM Snews 2.0 *
- * 18 Jun 93 1.11 MSM Heap debugging removed (using Bounds Check) *
- * 10 Jul 93 1.12 MSM New commands added, help updated *
- * 7 Aug 93 1.13 MSM Change command functionality to be TINish *
- * MSM Add expert mode *
- * MSM Add confirm exit mode *
- * 26 Aug 93 1.13 MSM Colour Support clean up, Expert toggle, bugs *
- * 3 Oct 92 1.14 MSM Case sensitive y/n questions made insensitive *
- * old style tab action added, new config options *
- * 132 col / 43 line support corrected *
- * article/thread save function corrected. *
- * 24 Nov 93 1.15 MSM Changed to Delta version control *
- * Session setting text changes *
- * 25 Nov 93 1.16 MSM Correct read list processing *
- * 5 Feb 94 AT2 GBJ Atari port. *
- * 2 Apr 94 1.17 MSM Add print thread support *
- * abort/replace/append choice for thread save *
- * 15.89 style added to smart match *
- * Save full newsgroup *
- * Suspend support *
- *****************************************************************************/
-
-
- #include <time.h>
- #include <io.h>
- #include <fcntl.h>
- #include "defs.h"
- #include "snews.h"
- #include "screen.h"
- #ifdef ATARI
- # include <sys/types.h>
- # include "st.h"
- # include "fileops.h"
- #else
- # include "exec.h"
- #endif
- #include "locking.h"
-
- #ifndef __TURBOC__
- #ifndef ATARI
- #include <graph.h>
- #include <dos.h>
- #define BLACK 0
- #define LIGHTGRAY 7
- unsigned long farcoreleft(void);
- #endif
- #endif
-
-
- extern int xfile;
-
- extern INFO my_stuff;
- #ifdef ATARI
- extern unsigned long _STACK;
- #else
- extern unsigned _stklen;
- #endif
- extern char search_text[];
- extern char save_name[];
- extern char search_group[];
-
- /*------------------------- find which thread to read ----------------------*/
- int select_thread(ACTIVE * gp, ARTICLE * head)
- {
-
- /*
- * Present the list of threads, and allow him to move up and down with
- * the arrow and PgUp and PgDn keys.
- */
-
- ARTICLE *top; /* thread at the top of the page */
- ARTICLE *this_thread; /* current thread */
- ARTICLE *th;
- ART_ID *art;
- int exit_code; /* why we are exiting the loop */
- char sub_tmp[80];
-
- int ch, i, idx, hit, a_ct, ch2;
-
- this_thread = head;
- top = head;
- exit_code = 0;
-
- show_threads(gp, &top, this_thread, TRUE, head);
-
- while (exit_code == 0) {
-
- ch = getch();
- switch (ch) {
-
- case 0:
- case 0xE0:
- ch = getch();
-
- switch (ch) {
-
- case Fn1:
- show_help(HELP_THREAD);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case Fn2:
- show_values();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case Fn3:
- change_values();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case UP_ARR:
- if (this_thread->last != NULL)
- this_thread = this_thread->last;
- break;
-
- case DN_ARR:
- if (this_thread->next != NULL)
- this_thread = this_thread->next;
- break;
-
- case PGUP:
- for (i = this_thread->index - top->index; i < PAGE_LENGTH; i++) {
- if (this_thread->last == NULL)
- break;
- this_thread = this_thread->last;
- }
- break;
-
- case PGDN:
- for (i = this_thread->index - top->index; i < PAGE_LENGTH; i++) {
- if (this_thread->next == NULL)
- break;
- this_thread = this_thread->next;
- }
- break;
-
- case HOME:
- top = this_thread = head;
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case END:
- top = this_thread = head;
- while (this_thread->next != NULL)
- this_thread = this_thread->next;
- break;
-
- case RT_ARR:
- read_thread(gp, this_thread, this_thread->art_num, 1);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case LT_ARR:
- exit_code = EX_QUIT;
- break;
- }
- break;
-
- case 'k':
- if (this_thread->last != NULL)
- this_thread = this_thread->last;
- break;
-
- case 'j':
- if (this_thread->next != NULL)
- this_thread = this_thread->next;
- break;
-
- case 2:
- case 21:
- case 'b':
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this_thread->last == NULL)
- break;
- this_thread = this_thread->last;
- }
- break;
-
- case 4:
- case 6:
- case ' ':
- for (i = 0; i < PAGE_LENGTH; i++) {
- if (this_thread->next == NULL)
- break;
- this_thread = this_thread->next;
- }
- break;
-
- case '!':
- textbackground(headb);
- textcolor(headf);
- clrscr();
- #ifdef ATARI
- printf("Sorry, function not supported. \n");
- printf("Press a key to return to Snews.\n\n");
- getch();
- #else
- printf("Type <EXIT> to return to Snews.\n\n");
- system("");
- #endif
- textbackground(textb);
- textcolor(textf);
- clrscr();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 's':
- xfile = FALSE;
- save_thread_to_disk(gp, this_thread);
- getch();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 'S':
- xfile = TRUE;
- save_thread_to_disk(gp, this_thread);
- xfile = FALSE;
- getch();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 'M':
- mail_to_someone(NULL);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 'v':
- #ifdef ATARI
- sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d LC5 compiled %s", rmj, rmm, rup, __DATE__);
- #else
- #ifdef __TURBOC__
- sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d BC++ compiled %s", rmj, rmm, rup, __DATE__);
- #else
- sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d MSVC compiled %s", rmj, rmm, rup, __DATE__);
- #endif
- #endif
- message(sub_tmp);
- getch();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 'w':
- strcpy(sub_tmp, "");
- if (gp->suspend == TRUE) {
- message("This group is suspended, post regardless ? (y/n) ");
- ch2 = getch();
- ch2 = tolower(ch2);
- message("");
- }
- else
- ch2 = 'y';
- if (ch2 == 'y') {
- post(NULL, gp->group, sub_tmp);
- show_threads(gp, &top, this_thread, TRUE, head);
- }
- break;
-
- case 'o':
- print_thread(gp, this_thread);
- message("-- Done --");
- break;
-
- case 'h':
- case 'H':
- show_help(HELP_THREAD);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case TAB:
- case 'N':
- if (my_stuff.tab_action == TRUE) {
-
- /*
- * Go to the next unread article. Work through each
- * thread, looking at each article to see if it's been
- * read
- */
-
- /* for each thread */
- th = this_thread;
- hit = FALSE;
- while (th != NULL) {
-
- art = th->art_num;
- a_ct = 0;
-
- /* for each article */
- while (art != NULL) {
- idx = (int) (art->id - gp->lo_num - 1);
- a_ct++;
- if (*((gp->read_list) + idx) == FALSE) {
- hit = TRUE;
- break;
- }
- art = art->next_art;
- }
- if (hit)
- break;
- th = th->next;
- }
-
- if (hit) {
- this_thread = th;
- read_thread(gp, this_thread, art, a_ct);
- show_threads(gp, &top, this_thread, TRUE, head);
- }
- else {
- exit_code = EX_NEXT;
- }
- break;
- }
- else { /* old style tab action */
- /*
- * Go to the next unread article. Work through each
- * thread, looking at each article to see if it's been
- * read
- */
-
- /* for each thread */
- th = this_thread;
- hit = FALSE;
- while (th != NULL) {
-
- art = th->art_num;
- a_ct = 0;
-
- /* for each article */
- while (art != NULL) {
- idx = (int)(art->id - gp->lo_num - 1);
- a_ct++;
- if ( *((gp->read_list)+idx) == FALSE) {
- hit = TRUE;
- break;
- }
- art = art->next_art;
- }
- if (hit) break;
- th = th->next;
- }
-
- if (hit) {
- this_thread = th;
- read_thread(gp, this_thread, art, a_ct);
- show_threads(gp, &top, this_thread, TRUE, head);
- } else {
- message("-- No more articles to read --");
- }
- break;
- }
-
- case 'P':
-
- /*
- * Go to the previous unread article. Work through each
- * thread, looking at each article to see if it's been
- * read
- */
-
- /* for each thread */
- th = this_thread->last;
- hit = FALSE;
- while (th != NULL) {
-
- art = th->art_num;
- a_ct = 0;
-
- /* for each article */
- while (art != NULL) {
- idx = (int) (art->id - gp->lo_num - 1);
- a_ct++;
- if (*((gp->read_list) + idx) == FALSE) {
- hit = TRUE;
- break;
- }
- art = art->next_art;
- }
- if (hit)
- break;
- th = th->last;
- }
-
- if (hit) {
- this_thread = th;
- read_thread(gp, this_thread, art, a_ct);
- show_threads(gp, &top, this_thread, TRUE, head);
- }
- else {
- message("-- No previous articles unread --");
- }
- break;
-
- case 'X':
- for(th=head;th!=this_thread;th=th->next)
- mark_thread_as_read(gp, th, TRUE);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 'K':
- mark_thread_as_read(gp, this_thread, FALSE);
- th = this_thread;
- hit = FALSE;
- while (th != NULL) {
-
- art = th->art_num;
- a_ct = 0;
-
- /* for each article */
- while (art != NULL) {
- idx = (int) (art->id - gp->lo_num - 1);
- a_ct++;
- if (*((gp->read_list) + idx) == FALSE) {
- hit = TRUE;
- break;
- }
- art = art->next_art;
- }
- if (hit)
- break;
- th = th->next;
- }
- if (th != NULL)
- this_thread = th;
- show_threads(gp, &top, this_thread, TRUE, head);
- if (th == NULL)
- message("-- No more articles to read --");
- break;
-
- case 'C':
- if (mark_group_as_read(gp, TRUE) == TRUE)
- exit_code = EX_NEXT;
- else
- lmessage("");
- break;
-
- case 'c':
- if (mark_group_as_read(gp, TRUE) == TRUE)
- show_threads(gp, &top, this_thread, TRUE, head);
- else
- lmessage("");
- break;
-
- case 'z':
- case 'Z':
- mark_thread_as_unread(gp, this_thread);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case ENTER:
- read_thread(gp, this_thread, this_thread->art_num, 1);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case BACKSP:
- art = this_thread->art_num;
- a_ct =1;
- while (art->next_art != NULL) {
- a_ct++;
- art = art->next_art;
- }
- read_thread(gp, this_thread, art, a_ct);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case '+':
- case '=':
- case '/':
- this_thread = search_thread(gp, this_thread, ch=='/', TRUE);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case '-':
- case '?':
- this_thread = search_thread(gp, this_thread, ch=='?', FALSE);
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case 'B':
- bug_report();
- show_threads(gp, &top, this_thread, TRUE, head);
- break;
-
- case ESCAPE:
- case 'q':
- exit_code = EX_QUIT;
- break;
- };
- if (exit_code == 0)
- show_threads(gp, &top, this_thread, FALSE, head);
- }
- return exit_code;
-
- }
-
-
- /*------------------------ mark thread as read ------------------------------*/
- int mark_thread_as_read(ACTIVE *gp, ARTICLE *this_thread, int q)
- {
-
- ART_ID *id;
- int idx, ch;
-
- if ((q == 0) && (my_stuff.expert != TRUE)) {
- message("Mark all articles in thread as read (y/n) [y] ? ");
- do
- {
- ch = getch();
- ch = tolower(ch);
- }
- while ((ch != 'y') && (ch != 'n') && (ch != ESCAPE) && (ch != '\r'));
- }
- else
- ch = 'y';
-
- if ((ch == 'y') || (ch == '\r')) {
- id = this_thread->art_num;
- while (id != NULL) {
- /* mark this article as read */
- idx = (int) ((id->id) - gp->lo_num - 1);
- *((gp->read_list) + idx) = (char) TRUE;
-
- id = id->next_art;
- }
- message("");
- return TRUE;
- }
- message("");
- return FALSE;
- }
-
- /*------------------------ mark thread as unread ------------------------------*/
- void mark_thread_as_unread(ACTIVE *gp, ARTICLE *this_thread)
- {
-
- ART_ID *id;
- int idx, ch;
-
- if (my_stuff.expert != TRUE) {
- message("Mark all articles in thread as unread (y/n) [y] ? ");
- do
- {
- ch = getch();
- ch = tolower(ch);
- }
- while ((ch != 'y') && (ch != 'n') && (ch != ESCAPE) && (ch != '\r'));
- }
- else
- ch = 'y';
-
- if ((ch == 'y') || (ch == '\r')) {
- id = this_thread->art_num;
- while (id != NULL) {
- /* mark this article as read */
- idx = (int) ((id->id) - gp->lo_num - 1);
- *((gp->read_list) + idx) = (char) FALSE;
-
- id = id->next_art;
- }
- }
- message("");
- }
-
-
- /*------------------------ save a newsgroup ------------------------------*/
- void save_group_to_disk(ACTIVE * gp)
- {
- char fnx[80];
- FILE *tmp, *tmp_file;
- char *fn, ch;
- char buf[512], prompt[80];
-
- sprintf(prompt, "Enter filename [%s] ", save_name);
- lmessage(prompt);
- gets(fnx);
- if (strlen(fnx) > 0)
- strcpy(save_name, fnx);
- else
- strcpy(fnx, save_name);
-
- tmp = NULL;
-
- if (access(fnx, 0) == 0) {
- message("File exists - append/replace/exit (a/r/e)? ");
- do {
- ch = getch();
- ch = tolower(ch);
- } while ((ch != 'a') && (ch != 'e') && (ch != 'r'));
- if (ch == 'e') {
- message("*** Save aborted - press any key to continue ***");
- getch();
- return;
- }
- if (ch == 'a') {
- if ((tmp = fopen(fnx, "at")) == NULL) {
- message("*** Cannot open file for appending - "
- "press any key to continue ***");
- getch();
- }
- }
- else {
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** Cannot open file for output - "
- "press any key to continue ***");
- getch();
- }
- }
- }
- else {
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** Cannot open file for output - "
- "press any key to continue ***");
- getch();
- }
- }
-
- if (tmp != NULL) {
- message("*** Saving Article(s) - please wait ***");
-
- fn = make_news_group_name(gp->group);
- if ((tmp_file = fopen(fn, "rb")) != NULL) {
- while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
- if (strncmp(buf, "@@@@END\n", 7) == 0)
- fputc(0x0c, tmp);
- else
- fputs(buf, tmp);
- }
- fclose(tmp_file);
- }
- fclose(tmp);
- message("Article(s) saved - press any key to continue");
- getch();
- }
- }
-
- /*------------------------ save a thread ------------------------------*/
- void save_thread_to_disk(ACTIVE * gp, ARTICLE * this_thread)
- {
- ART_ID *id;
- char *fn;
- int a_ct;
- char fnx[80];
- int ch;
- FILE *tmp, *tmp_file;
- time_t now;
- struct tm *tmnow;
- char timestr[64];
- char buf[512], prompt[128];
-
- if (xfile == TRUE) { /* extract function */
- strcpy(fnx, my_stuff.mail_dir);
- strcat(fnx, my_stuff.extruser);
- strcat(fnx, ".txt");
- }
- else {
- sprintf(prompt, "Enter filename? [%s] ", save_name);
- lmessage(prompt);
- gets(fnx);
- if (strlen(fnx) > 0)
- strcpy(save_name, fnx);
- else
- strcpy(fnx, save_name);
- }
-
- tmp = NULL;
-
- if (xfile == TRUE) { /* extract function */
- if (access(fnx, 0) == 0) {
- if ((tmp = fopen(fnx, "at")) == NULL) {
- message("*** Cannot open file for appending -"
- "press any key to continue ***");
- getch();
- }
- }
- else {
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** Cannot open file for output - press a key to continue ***");
- getch();
- }
- }
- }
- else { /* save function */
- if (access(fnx, 0) == 0) {
- message("File exists - append/replace/exit(a/r/e)? ");
- do
- {
- ch = getch();
- ch = tolower(ch);
- }
- while ((ch != 'a') && (ch != 'e') && (ch != 'r'));
- if (ch == 'e') {
- message("*** Save aborted - press any key to continue ***");
- getch();
- return;
- }
- if (ch == 'a') {
- if ((tmp = fopen(fnx, "at")) == NULL) {
- message("*** Cannot open file for appending - "
- "please any key to continue ***");
- getch();
- }
- }
- else {
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** Cannot open file for output - press a key to continue ***");
- getch();
- }
- }
- }
- else {
- if ((tmp = fopen(fnx, "wt")) == NULL) {
- message("*** Cannot open file for output - press a key to continue ***");
- getch();
- }
- }
- }
-
- if (tmp != NULL) {
-
- message("*** Saving Article(s) - please wait ***");
-
- fn = make_news_group_name(gp->group);
-
- id = this_thread->art_num;
- a_ct = 0;
-
- while (id != NULL) {
-
- if (xfile == TRUE) {
- tzset();
- time(&now);
- tmnow = localtime(&now);
- strftime(timestr, sizeof(timestr), "%a %b %d %H:%M:%S %Z %Y", tmnow);
- fprintf(tmp, "From snews@%s.%s %s\n", my_stuff.my_site,
- my_stuff.my_domain, timestr);
- }
-
- if ((tmp_file = fopen(fn, "rb")) != NULL) {
-
- fseek(tmp_file, id->art_off, SEEK_SET);
- while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
- if (strncmp(buf, "@@@@END\n", 7) == 0)
- break;
- fputs(buf, tmp);
- }
- fclose(tmp_file);
-
- }
-
- fputs("\n\n", tmp);
-
- a_ct++;
- id = id->next_art;
-
- }
-
- fclose(tmp);
- message("Article(s) saved - press any key to continue");
- }
- }
-
- /*------------------------ search through articles ------------------------*/
- int search_message(char *fn, ART_ID *current, int forward)
- {
- int offset = 0, found = FALSE;
- TEXT *tx;
- LINES *text;
- char prompt[128], pattern[128];
- int irq = FALSE;
-
- sprintf(prompt, "Search %s for [%s] ",
- forward ? "forward" : "backward", search_text);
- lmessage(prompt);
- gets(pattern);
- if (pattern[0] == 0x1b)
- return(0);
-
- if (strlen(pattern) > 0)
- strcpy(search_text, pattern);
- else
- strcpy(pattern, search_text);
-
- strlwr(pattern);
-
- message("*** searching - please wait (press <ESC> to abort ***");
-
- for (;;) {
-
- current = forward ? current->next_art : current->prev_art;
- if (current == NULL)
- break;
- offset++;
- tx = load_article(fn, current->art_off, FALSE);
-
- for (text=tx->start; !found && text!=NULL; text=text->next) {
- strlwr(text->data);
- found = (strstr(text->data, pattern) != NULL);
- }
-
- free_article(tx);
-
- if (kbhit())
- irq = (getch() == 27);
-
- if (found || irq)
- break;
- }
-
- if (!found) {
- sprintf(prompt, "*** '%s' not found - press any key ***", pattern);
- message(prompt);
- getch();
- }
-
- return found ? offset : 0;
- }
-
- /*------------------------ read a thread ------------------------------*/
- int read_thread(ACTIVE * gp, ARTICLE * this_thread, ART_ID * first, int a_ct)
- {
- ART_ID *id;
- char *fn;
- int idx, res;
- TEXT *tx;
- char author[128], msg_id[128], r_name[128];
- CROSS_POSTS *h, *h0;
- ACTIVE *gx;
- int junk_flag; /* nz - going to junk group */
-
- fn = make_news_group_name(gp->group);
-
- id = first;
-
- while (id != NULL) {
- tx = load_article(fn, id->art_off, TRUE);
- res = read_article(gp, tx, this_thread, a_ct, id);
-
- if (res & EX_NOTREAD)
- res &= ~EX_NOTREAD;
- else {
- /* mark this article as read */
- idx = (int) ((id->id) - gp->lo_num - 1);
- *((gp->read_list) + idx) = (char) TRUE;
-
- /* mark the crossposts */
- get_his_stuff(tx, author, msg_id, r_name);
- if ((h0 = look_up_history(msg_id, gp->group)) != NULL) {
- h = h0;
- while (h != NULL) {
- gx = find_news_group(h->group, &junk_flag);
- idx = (int) ((h->art_num) - gx->lo_num - 1);
- *((gx->read_list) + idx) = (char) TRUE;
- h = h->next;
- }
-
- free_cross_post_list(h0);
- h0 = NULL;
- }
- }
-
- DBGOUT(("%s:%d\n", __FILE__, __LINE__));
-
- free_article(tx);
- tx = NULL;
-
- switch (res) {
- case EX_QUIT:
- id = NULL;
-
- case EX_NEXT_UNREAD:
- while (id != NULL) {
- idx = (int) (id->id - gp->lo_num - 1);
- if (*((gp->read_list) + idx) == FALSE) {
- break;
- }
- a_ct++;
- id = id->next_art;
- }
-
- if (id == NULL)
- message("-- No more articles in thread --");
-
- break;
-
- case EX_SEARCH_FORW:
- res = search_message(fn, id, 1);
- while (res--) {
- a_ct++;
- id = id->next_art;
- }
- break;
-
- case EX_SEARCH_BACKW:
- res = search_message(fn, id, 0);
- while (res--) {
- a_ct--;
- id = id->prev_art;
- }
- break;
-
- case EX_PREV:
- if (id->prev_art) {
- id = id->prev_art;
- a_ct--;
- }
- break;
-
- case EX_PREVIOUS:
- a_ct--;
- id = id->prev_art;
- break;
-
- case EX_FIRST:
- while (id->prev_art != NULL) {
- id = id->prev_art;
- a_ct--;
- }
- break;
-
- case EX_LAST:
- while (id->next_art != NULL) {
- id = id->next_art;
- a_ct++;
- }
- break;
-
- default: /* EX_NEXT */
- a_ct++;
- id = id->next_art;
- break;
- } /* switch (res) */
-
- }
-
- return (res);
- }
-
- /*--------------------- smarter subject compare --------------------------*/
- int strip_off_part(char *str)
- {
- char *ptr = str + strlen(str);
-
- /* strip off (case-insensitive) things like:
- * - "Part01/10"
- * - "Part 01/10"
- * - "Part 01 of 10"
- * - "[1/10]"
- * - "(1 of 10)"
- * - "1 of 10"
- * - "Patch02a/04"
- * - "Patch20"
- */
-
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str && (ptr[-1] == ')' || ptr[-1] == ']') )
- ptr--;
-
- while ( ptr > str && isdigit(ptr[-1]) )
- ptr--;
-
- if ( !isdigit(*ptr) )
- return 0;
-
- if ( ptr > str && ptr[-1] == '/' )
- ptr--;
- if ( ptr > str && ptr[-1] == '.' )
- ptr--;
- else if ( ptr > str + 3 && strnicmp(ptr - 4, " of ", 4) == 0 )
- ptr -= 4;
- else if ( ptr > str + 4 && strnicmp(ptr - 5, "Patch", 5) == 0 ) {
- ptr -= 5;
- goto label;
- }
- else if ( ptr > str + 5 && strnicmp(ptr - 6, "Patch ", 6) == 0 ) {
- ptr -= 6;
- goto label;
- }
- else
- return 0;
-
- if ( ptr > str && 'a' <= ptr[-1] && ptr[-1] <= 'z' )
- ptr--;
-
- while ( ptr > str && isdigit(ptr[-1]) )
- ptr--;
-
- if ( !isdigit(*ptr) )
- return 0;
-
- if ( ptr > str && (ptr[-1] == '(' || ptr[-1] == '[') )
- ptr--;
-
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str + 3 && strnicmp(ptr - 4, "Part", 4) == 0 )
- ptr -= 4;
-
- label:
- while ( ptr > str && ptr[-1] == ' ' )
- ptr--;
-
- if ( ptr > str && ptr[-1] == ',' )
- ptr--;
- else if ( ptr > str && ptr[-1] == ':' )
- ptr--;
-
- *ptr = 0;
- return 1;
- }
-
- /*------------------------------------------------------------------------*/
- char *skip_vi(char *str)
- {
- char *ptr = str;
-
- /* skip things like "v02i0027: " */
-
- while ( isspace(*ptr) )
- ptr++;
-
- if ( *ptr++ != 'v' )
- return str;
-
- if ( !isdigit(*ptr) )
- return str;
-
- while ( isdigit(*ptr) )
- ptr++;
-
- if ( *ptr++ != 'i' )
- return str;
-
- if ( !isdigit(*ptr) )
- return str;
-
- while ( isdigit(*ptr) )
- ptr++;
-
- if ( *ptr++ != ':' )
- return str;
-
- if ( *ptr++ != ' ' )
- return str;
-
- while ( isspace(*ptr) )
- ptr++;
-
- return ptr;
- }
-
- /*------------------------------------------------------------------------*/
- int smartcmp(char *str1, char *str2)
- {
- int slen;
- char s1[256], s2[256];
-
- strcpy(s1, str1);
- strcpy(s2, str2);
- if (strip_off_part(s1) && strip_off_part(s2)) {
- str1 = skip_vi(s1);
- str2 = skip_vi(s2);
- }
-
- slen = (int) (strlen(str1) < strlen(str2))
- ? strlen(str1) : strlen(str2);
-
- if (my_stuff.match_len == -1)
- if (strlen(str1) <= strlen(str2))
- return -1;
- else
- return 1;
-
- if (my_stuff.match_len == 0)
- slen = 128;
-
- if (--slen < my_stuff.match_len)
- slen = my_stuff.match_len;
- return (strnicmp(str1, str2, slen));
-
- }
-
- /*------------------------- read the headers --------------------------*/
- ARTICLE *get_headers(ACTIVE * gp)
- {
-
- /*
- * Read the files and get the headers
- */
-
- char *fn;
- char buf[512], fnx[256], *buf_p;
- long g, n_read;
- FILE *tmp_file;
-
- ARTICLE *start, *that, *tmp;
- ARTICLE **ptr;
- ART_ID *art_this, *art_new;
- int ct_art, cmp;
-
- n_read = 0;
- ct_art = 0;
- start = NULL;
-
- fn = make_news_group_name(gp->group);
- sprintf(fnx, "%s.IDX", fn);
-
- if ((tmp_file = fopen(fnx, "rb")) != NULL) {
-
- for (g = gp->lo_num + 1; g <= gp->hi_num; g++) {
-
- if ((n_read++ % 10) == 0) {
- gotoxy(1, scr_rows);
- printf("%d articles processed", n_read - 1);
- }
-
- /*
- * Read the index Search the linked list for the subject
- * - allocate a new subject if necessary
- * - add to the existing list
- */
-
- if (fgets(buf, 255, tmp_file) == NULL) {
- gotoxy(1, scr_rows);
- fprintf(stderr, "\nsnews: index file is corrupt (%s)\n", gp->group);
- exit(1);
- }
- /* check all is in sync */
- if (g != atol(buf + 9)) {
- gotoxy(1, scr_rows);
- fprintf(stderr, "\nsnews: in %s article %ld found when %ld"
- " expected\n", gp->group, atol(buf + 9), g);
- exit(1);
- }
- /* skip the two eight digit numbers and the 9 and the spaces */
- buf_p = buf + 28;
-
- eat_gunk(buf_p);
-
- for (tmp=start; tmp != NULL; ) {
-
- cmp = smartcmp(buf_p, tmp->header);
-
- if (cmp > 0) {
- if (tmp->left)
- tmp = tmp->left;
- else {
- ptr = &(tmp->left);
- tmp = NULL;
- break;
- }
- }
- else if (cmp < 0) {
- if (tmp->right)
- tmp = tmp->right;
- else {
- ptr = &(tmp->right);
- tmp = NULL;
- break;
- }
- }
- else {
- /* found this subject */
- tmp->num_articles++;
-
- /* allocate new article */
- art_new = (ART_ID *) malloc(sizeof(ART_ID));
- art_new->id = g;
- art_new->art_off = atol(buf);
-
- /* place it at end */
- art_this = tmp->art_num;
- while (art_this->next_art != NULL) {
- art_this = art_this->next_art;
- }
- art_this->next_art = art_new;
-
- art_new->prev_art = art_this;
- art_new->next_art = NULL;
-
- break;
- }
- }
-
- if (tmp == NULL) {
- /* not found - allocate new thread */
-
- if (start == NULL) {
- start = (ARTICLE *) malloc(sizeof(ARTICLE));
- that = start;
- that->last = NULL;
- }
- else {
- ct_art++;
- that->next = (ARTICLE *) malloc(sizeof(ARTICLE));
- that->next->last = that;
- that = that->next;
- *ptr = that;
- }
-
- that->next = NULL;
- that->index = ct_art;
- that->left = that->right = NULL;
-
- /* store article data */
- strcpy(that->header, buf_p);
- that->num_articles = 1;
- that->art_num = (ART_ID *) malloc(sizeof(ART_ID));
- that->art_num->prev_art = NULL;
- that->art_num->next_art = NULL;
- that->art_num->id = g;
- that->art_num->art_off = atol(buf);
-
- }
- }
- fclose(tmp_file);
- }
- else {
- gotoxy(1, scr_rows);
- fprintf(stderr, "\nsnews: can't open index file %s\n", fnx);
- exit(1);
- }
-
- gp->threads = ct_art+1;
-
- return (start);
-
- }
-
-
-
- /*------------------------ clean up subject line ----------------------------*/
- void eat_gunk(char *buf)
- {
-
- /*
- * This routine take the header line, and strips the word
- * header word, 'Re:', and any extra blanks, trim to 59
- * chars
- */
-
- char *p, tmp[256];
-
- buf[58] = 0;
-
-
- /* strip the Subject: etc off the front */
- /* while ((strstr(buf, "Re:") != NULL) ||
- (strstr(buf, "re:") != NULL) ||
- (strstr(buf, "RE:") != NULL) || */
- while ((strnicmp(buf, "re:", 3) == 0) ||
- (strnicmp(buf, "From:", 5) == 0) ||
- (strnicmp(buf, "Path:", 5) == 0) ||
- (strnicmp(buf, "Subject:", 8) == 0) ||
- (strnicmp(buf, "Organization:", 13) == 0) ||
- (strnicmp(buf, "Organisation:", 13) == 0) ||
- (strnicmp(buf, "Followup-To:", 12) == 0)) {
- p = strstr(buf, ":");
- strcpy(buf, p + 1);
- }
-
- strcpy(tmp, buf);
- *buf = '\x00';
- p = strtok(tmp, " \t\n\r");
-
- while (p != NULL) {
-
- if (stricmp(p, "Re:") != 0) {
- strcat(buf, p);
- strcat(buf, " ");
- }
- else {
- if (strlen(buf) > 0) {
- strcat(buf, p);
- strcat(buf, " ");
- }
- }
- p = strtok(NULL, " \t\n\r");
- }
-
- if (strlen(buf) <= 1)
- strcpy(buf, "** no subject **");
- else
- buf[strlen(buf)-1] = '\0'; /* remove trailing space */
- }
-
-
- /*-------------------- release the subject structures ---------------------*/
- void free_header(ARTICLE * start)
- {
-
- /*
- * Work our way through the subject structure releasing all the memory
- */
-
- ARTICLE *a, *t;
- ART_ID *u, *v;
-
- a = start;
-
- while (a != NULL) {
-
- u = a->art_num;
- while (u != NULL) {
- v = u->next_art;
- free(u);
- u = v;
- }
-
- t = a->next;
- free(a);
- a = t;
- }
- }
-
-
- /*------------------- count unread articles in a thread --------------------*/
- int count_unread_in_thread(ACTIVE * gp, ARTICLE * a)
- {
-
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- ART_ID *id;
- int ct, idx;
-
- ct = 0;
- id = a->art_num;
-
- while (id != NULL) {
- idx = (int) (id->id - gp->lo_num - 1);
- if (*((gp->read_list) + idx) == FALSE)
- ct++;
- id = id->next_art;
- }
-
- return (ct);
- }
-
-
- /*------------------- count unread articles in a group --------------------*/
- int count_unread_in_group(ACTIVE * gp)
- {
-
- /*
- * Take the thread 'a' for the given group 'gp' and return the number
- * of unread articles
- */
-
- int articles, ct, i;
-
- ct = 0;
- articles = (int) (gp->hi_num - gp->lo_num);
-
- for (i = 0; i < articles; i++) {
- if (*((gp->read_list) + i) == FALSE)
- ct++;
- }
-
- return (ct);
- }
-
-
-
- /*------------------- mark unread articles in a group --------------------*/
- int mark_group_as_read(ACTIVE * gp, int ask)
- {
-
- /*
- * Take the given group 'gp' and mark all the unread articles
- * as read.
- */
-
- int articles, i, ch;
-
- if ((ask == TRUE) && (my_stuff.expert != TRUE)) {
- message("Mark all articles in group as read (y/n) [y] ? ");
- do
- {
- ch = getch();
- ch = tolower(ch);
- }
- while ((ch != 'y') && (ch != 'n') && (ch != 'q') && (ch != ESCAPE) && (ch != '\r'));
- } else
- ch = 'y';
-
- if ((ch == 'y') || (ch == '\r')) {
-
- articles = (int) (gp->hi_num - gp->lo_num);
-
- for (i = 0; i < articles; i++)
- *((gp->read_list) + i) = (char) TRUE;
-
- return TRUE;
- }
- return FALSE;
- }
-
- /*----------------- mark read articles in a group as unread ----------------*/
- void mark_group_as_unread(ACTIVE * gp)
- {
-
- /*
- * Take the given group 'gp' and mark all the read articles as
- * unread.
- */
-
- int articles, i, ch;
-
- if (my_stuff.expert != TRUE) {
- message("Mark all articles in group as unread (y/n) [y] ? ");
- do
- {
- ch = getch();
- ch = tolower(ch);
- }
- while ((ch != 'y') && (ch != 'n') && (ch != 'q') && (ch != ESCAPE) && (ch != '\r'));
- }
- else
- ch = 'y';
-
- if ((ch == 'y') || (ch == '\r')) {
-
- articles = (int) (gp->hi_num - gp->lo_num);
-
- for (i = 0; i < articles; i++)
- *((gp->read_list) + i) = (char) FALSE;
-
- }
- }
-
- /*-------------------------- print a thread ---------------------------------*/
- void print_thread(ACTIVE *gp, ARTICLE *this_thread)
- {
- ART_ID *id;
- char *fn;
- int a_ct;
- FILE *tmp_file;
- char buf[512];
-
- message("*** printing Article(s) - please wait ***");
-
- fn = make_news_group_name(gp->group);
-
- id = this_thread->art_num;
- a_ct = 0;
-
- while (id != NULL) {
-
- if ((tmp_file = fopen(fn, "rb")) != NULL) {
-
- fseek(tmp_file, id->art_off, SEEK_SET);
- while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
- if (strncmp(buf, "@@@@END\n", 7) == 0)
- break;
- fputs(buf, stdprn);
- fputc(0x0d, stdprn);
- }
- fputc(0x0c, stdprn);
- fflush(stdprn);
- }
-
- a_ct++;
- id = id->next_art;
- }
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void message(char *msg)
- {
- int x;
-
- gotoxy(1, scr_rows - 1);
- textbackground(msgb);
- textcolor(msgf);
- clreol();
-
- x = (scr_cols - strlen(msg)) / 2;
- gotoxy(x, scr_rows - 1);
- cprintf("%s", msg);
- textbackground(textb);
- textcolor(textf);
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void lmessage(char *msg)
- {
- gotoxy(1, scr_rows - 1);
- textbackground(msgb);
- textcolor(msgf);
- clreol();
- cprintf("%s", msg);
- textbackground(textb);
- textcolor(textf);
- }
-
-
- /*-------------------------- status message ---------------------------------*/
- void command(char *msg)
- {
- int x;
-
- gotoxy(1, scr_rows);
- textbackground(msgb);
- textcolor(msgf);
- clreol();
-
- x = (scr_cols - strlen(msg)) / 2;
- gotoxy(x, scr_rows);
- cprintf(msg);
-
- textbackground(textb);
- textcolor(textf);
- }
-
- #ifndef ATARI
- /****************************************************************************
- * system *
- * Replacement for the standard system () function. *
- * Error code added MSM 3 Jun 93 *
- *****************************************************************************/
- int system(const char *cmd)
- {
- char *p, *q; /* -> command and parameters */
- unsigned int rc; /* return value */
- char prompt[80]; /* Error message buffer */
-
- if (strlen(cmd) == 0) {
- rc = do_exec("", "", USE_ALL, 0xffff, NULL);
- return(rc);
- }
-
- p = strdup(cmd);
- if (p == NULL)
- return (0x102); /* no memory */
-
- q = p;
- while (*q != ' ' && *q != '\t' && q != '\0')
- q++;
-
- if (*q != '\0') {
- *q = '\0';
- q++; /* q -> parameters */
- }
- rc = do_exec(p, q, USE_ALL, 0xffff, NULL);
-
- if (rc > (unsigned) 0xff) {
- sprintf(prompt, "Unable to run command \"%s %s\"", p, q);
- message(prompt);
- getch();
- }
-
- #if defined (free)
- #undef free /* in case of debugging package! */
- #endif
- free(p);
- return (rc);
- } /* int system (const char *cmd) */
-
-
- #ifndef __TURBOC__
-
- unsigned long farcoreleft(void)
- {
-
- union _REGS inregs, outregs;
-
- _heapmin();
- inregs.h.ah = 0x48;
- inregs.x.bx = 0xffff;
- _intdos(&inregs, &outregs);
-
- return (16l * (long)outregs.x.bx);
- }
-
- unsigned long testcoreleft(void)
- {
-
- union _REGS inregs, outregs;
-
- inregs.h.ah = 0x48;
- inregs.x.bx = 0xffff;
- _intdos(&inregs, &outregs);
-
- return (16l * (long)outregs.x.bx);
- }
-
- #endif
-
- #endif
-